#pragma once
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <vector>
#include <string>
#include <sqlite3.h>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>
#include <sys/stat.h>

#include "Log.h"
using namespace std;

namespace ns_util
{
    class SqliteHelper
    {
        // void* : 是设置的在回调时传入的 arg 参数
        // int：一行中数据的列数
        // char**：存储一行数据的字符指针数组
        // char**：每一列的字段名称
        // 回调函数也有返回值(返回值只有为SQLITE_OK才表示成功)
        typedef int (*SqliteCallback)(void *, int, char **, char **);

    public:
        SqliteHelper(const string &dbfile)
            : _dbfile(dbfile), _sq(nullptr)
        {
        }

        ~SqliteHelper()
        {
            close();
        }

        // #define SQLITE_OK           0   /* Successful result */
        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_sq,
                                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);
            if (ret != SQLITE_OK)
            {
                ns_log::LOG(FATAL) << "创建/打开SQLITE数据库失败: " << sqlite3_errmsg(_sq) << endl;
                return false;
            }
            return true;
        }
        bool exec(const string &sql_statement, const SqliteCallback &callback, void *arg)
        {
            int ret = sqlite3_exec(_sq, sql_statement.c_str(), callback, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                ns_log::LOG(WARNING) << sql_statement << " 执行该语句失败: " << sqlite3_errmsg(_sq) << endl;
                return false;
            }
            return true;
        }
        void close()
        {
            sqlite3_close_v2(_sq);
        }

    private:
        string _dbfile;
        sqlite3 *_sq; // 数据库句柄
    };

    // 字符串分割
    class StrSplit
    {
    public:
        static size_t Split(const string &str, const string &sep, vector<string> &ret)
        {
            size_t begin = 0, pos = 0;
            while (begin < str.size())
            {
                pos = str.find(sep, begin);
                if (pos == string::npos)
                {
                    ret.push_back(str.substr(begin));
                    break;
                }
                string tmp = str.substr(begin, pos - begin);
                if (pos != begin) // 防止begin指的位置就是sep的起始处,从而push了一个空串
                    ret.push_back(move(tmp));

                begin = pos + sep.size();
            }

            return ret.size();
        }
    };

    // 生成唯一识别码
    class Uuid
    {
    public:
        // 生成8个随机数字(8 4 4)，加上8字节序号(4 12)共16字节数组生成32位16进制字符
        static string uuid()
        {
            // 1.8个一字节的随机数
            random_device rd;
            // 使用机器随机数作为种子为随机数生成
            mt19937_64 mt(rd());
            // 每一字节的数据范围(0,255)
            uniform_int_distribution<int> distri(0, 255);
            stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << setw(2) << setfill('0') << hex << distri(mt);
                if (i == 3 || i == 5 || i == 7)
                    ss << '-';
            }

            // 2.8字节序号
            static atomic<size_t> seq(1);  // 声明成静态，延长生命周期
            size_t num = seq.fetch_add(1); // 类似于后置++
            ss << setw(4) << setfill('0') << hex << (num >> (6 * 8));
            ss << '-';
            ss << setw(12) << setfill('0') << hex << num;

            return ss.str();
        }
    };

    class FileOper
    {
    public:
        static bool exit(const string &filename)
        {
            struct stat st;
            return stat(filename.c_str(), &st) == 0;
        }
        static size_t size(const string &filename)
        {
            struct stat st;
            int ret = stat(filename.c_str(), &st);
            if (ret == -1)
            {
                ns_log::LOG(WARNING) << "文件不存在" << endl;
                return 0;
            }
            return st.st_size;
        }
        static bool read(const string &filename, char *content)
        {
            return read(filename, content, 0, size(filename));
        }
        static bool read(const string &filename, char *content, size_t offset, size_t len)
        {
            ifstream in(filename, ios::binary | ios::in);
            if (!in.is_open())
            {
                ns_log::LOG(FATAL) << "读取时文件打开失败" << endl;
                std::exit(-1);
            }
            // 跳转读取位置
            in.seekg(offset, ios::beg);
            in.read(content, len); // c_str类型是const char*
            // 判断文件读取情况
            if (in.good() == false)
            {
                ns_log::LOG(WARNING) << "文件读取失败" << endl;
                in.close();
                return false;
            }
            in.close();
            return true;
        }
        static bool write(const string &filename, const char *content, size_t len)
        {
            return write(filename, content, 0, len);
        }
        static bool write(const string &filename, const char *content, size_t offset, size_t len)
        {
            // ofstream无读权限无法跳转到文件指定位置
            // fstream具有文件读写权限
            fstream out(filename, ios::binary | ios::in | ios::out);
            if (!out.is_open())
            {
                ns_log::LOG(FATAL) << "写入时文件打开失败" << endl;
                std::exit(-1);
            }
            // 跳转写入位置
            out.seekp(offset, ios::beg);
            out.write(content, len);
            // 判断文件写入情况
            if (out.good() == false)
            {
                ns_log::LOG(WARNING) << "文件写入失败" << endl;
                out.close();
                return false;
            }
            out.close();
            return true;
        }
        static string parentdir(const string &filename)
        {
            size_t pos = filename.rfind("/");
            if (pos == string::npos)
            {
                // test.txt文件,当前位置就看作父级目录
                return "./";
            }
            return filename.substr(0, pos + 1); // 涵盖斜杠/
        }
        static bool rename(const string &filename, const string &newname)
        {
            return ::rename(filename.c_str(), newname.c_str()) == 0;
        }
        static bool createfile(const string &filename)
        {
            // 使用ofstream的文件不存在时会创建文件
            ofstream out(filename, ios::app | ios::binary);
            if (!out.is_open())
            {
                ns_log::LOG(FATAL) << "写入时文件打开失败" << endl;
                std::exit(-1);
            }
            out.close();
            return true;
        }
        static bool removefile(const string &filename)
        {
            return remove(filename.c_str()) == 0;
        }
        static bool createdir(const string &path)
        {
            // /a/b/c/
            // 创建目录时可能上一级目录就不存在,所以需要逐级去寻找
            int begin = 0, end = 0;
            while (begin < path.size())
            {
                end = path.find('/', begin);
                if (end == begin) // 起初begin就是指向/位置
                {
                    begin = end + 1;
                    continue;
                }
                int ret = mkdir(path.substr(0, end).c_str(), 0775);
                if (ret == -1 && errno != EEXIST)
                {
                    ns_log::LOG(WARNING) << "目录创建失败: " << strerror(errno) << endl;
                    return false;
                }
                begin = end + 1;
                if (end == string::npos)
                    break;
            }
            return true;
        }
        static bool removedir(const string &filename)
        {
            string cmd = "rm -rf " + filename;
            return system(cmd.c_str()) != -1; // 调用系统命令
        }
    };
}